/******************************************************************************\
 *                                                                            *
 *                       Craftmesh Geometric Engine                           *
 *                   Flavio Bertini fl.bertini@gmail.com                      *
 *                                                                            *
 *                                                                            *
\******************************************************************************/


#if !defined(__Craftmesh_gengine_h__)
#define __Craftmesh_gengine_h__


#include <limits>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <GL/freeglut.h>
#include "Const.h"
#include "Math.h"
#include "Craftmesh_model.h"
#include "Craftmesh_controller.h"

#define TOLL 2.0e-6

#define EM1FM2  0
#define EM2FM1  1

#define MINOR(x,y) ((x) <= (y) ? (x) : (y))
#define MAJOR(x,y) ((x) >= (y) ? (x) : (y))

// #define DEBUG_LOAD 1
#define DEBUG_GLUE 1


using namespace std;


struct q_point {  /* Struttura per memorizzare successioni di punti */
    Vertex v;
    int indexFace;
    int indexEdge;
    int tan;
    int ins;				/* indica se è stato inserito in un edge */
    struct q_point *next;
};

struct e_bound {  /* Struttura per creare la coda degli edge di bordo */
  int edge;
  struct e_bound *next;
  struct e_bound *prev;
  struct q_point *insV;
};

struct i_point {  /* Struttura per memorizzare successioni di punti
                               di intersezione di due mesh, definendo
                               una poligonale */
    struct q_point *point;
    char queueMesh;       /* indica a quali mesh fanno riferimento le
                             informazini del Q_POINT *point */
    int indexV;           /* indica l'indice con cui sarà inserito nella mesh */
    struct i_point *next;
    struct i_point *prev;
};

struct queue {
    int elem;
    struct queue *next;
  };

// invert selected vertecies
void gen_invertSelectPoint(void);

// select point by plane
void gen_selectPointByPlane(int);

// select point by sphere
void gen_selectPointBySphere(int);

// deselect all vertices
void gen_deselectAllVertices(void);

// Detect edges belonging to more than two faces. TRUE if Two-Manifold
bool gen_TwoManifold(Mod_SubdivMesh *mesh);

// Compute normal between AB and BC vectors, saving it normalized in N
void gen_computeNormal(Vertex a, Vertex b, Vertex c, Vertex *n);

/* Funzione che determina su quale piano coordinato proiettare una face,
   per poterne massimizzare l'area. */
char gen_maximizeProjection(Vertex P1, Vertex P2, Vertex P3);

/* Funzione che determina se una face è convessa o concava. */
int* gen_shapeFace(Mod_SubdivMesh *mesh, int f, bool *test);

/* Funzione che determina se i vertici di una faccetta sono complanari. */
bool gen_complaneTest(Mod_SubdivMesh *mesh, int f, double *param);

/* Funzione che determina se una faccia giace su un piano */
bool gen_complaneFace(Mod_SubdivMesh *mesh, int f, double *param);

/* Funzione che determina se un insieme di vertici è complanare */
bool gen_complaneVectorTest(vector<Vertex> v);

/* Funzione che determina se la face presenta un'intersezione tra i suoi edge;
   per rilevarlo determina se gli angoli creati di volta in volta in ogni
   vertice dai successivi, risultano crescenti. */
bool gen_crossEdge(Mod_SubdivMesh *mesh, int f);

// Funzione che rileva tutti gli elementi della mesh che, data la tolleranza,rientrano in un intorno di P
void gen_inquiryElementsMesh(Mod_SubdivMesh *mesh, Vertex P, float toll);

/* Funzione che determina se la distanza di un vertex da
   un edge, definito dai suoi vertici, rientra in un valore
   di tolleranza. */
bool gen_pointDsegment(Vertex v, Vertex p0, Vertex p1, float e);

/* Funzione che determina se la distanza di un vertex da una retta,
 * individuata da due suoi punti, rientra in un valore di tolleranza. */
bool gen_pointDline(Vertex v, Vertex p0, Vertex p1, float e);

/* Funzione che determina se la distanza di un punto da una face,
   definita dai suoi vertici, rientra in un valore di tolleranza. */
bool gen_pointDplane(Vertex P, double *param, Mod_SubdivMesh *mesh, int f, float e);

/* Funzione che determina se un punto è interno ad una face convessa*/
bool gen_pointDplane2D(Vertex q, Mod_SubdivMesh *mesh, int f, float e);

/* Funzione che determina se un punto è interno ad una face convessa individuata dai suoi vertici */
bool gen_pointDplane2DV(Vertex q, vector<Vertex> vert, float e);

/* Verifica se un punto è interno a un poligono (concavo o convesso) */
bool gen_pnpoly(Mod_SubdivMesh *mesh, int f, Vertex v);

/* Funzione che determina se la distanza di un punto da una face,
   definita dai suoi vertici non complanari, rientra in un valore
   di tolleranza. */
bool gen_pointDiperPlane(Vertex P, Mod_SubdivMesh *mesh, int f, float e);

/* Procedura di ristrutturazione della mesh, vengono eliminati
   i vertici ridondanti.
   ATTENZIONE: in alcuni casi questa attività è da svolgersi prima
               della chimata alla procedura LoadSubdivMesh(). */
Mod_SubdivMesh* gen_collapseVmiss(Mod_SubdivMesh *mesh, float toll, bool onlyBound);

/* Procedura di ristrutturazione della mesh, vengono eliminati
   i vertici inutilizzati. */
Mod_SubdivMesh* gen_eraseVmiss(Mod_SubdivMesh *mesh);

/* Procedura di ristrutturazione della mesh, viene cambiata la valenza alle
   faccette che presentano vertici uguali ripetuti. */
Mod_SubdivMesh* gen_eraseRepeatVinF(Mod_SubdivMesh *mesh);

/* Procedura per la ricerca delle intersezioni tra
   edge e face di due differenti mesh. */
q_point* gen_edgeIntersectFace(Mod_SubdivMesh *mesh_1, Mod_SubdivMesh *mesh_2);

/* Procedura che individua il punto di intersezione
   tra un segmento e una face definita da un piano. */
bool gen_segmentCrossPolygon(Mod_SubdivMesh *mesh, int f, Vertex P, Vertex Q, Vertex *C);

/* Procedura che suddivide una face triangolare della mesh. */
void gen_splitTriangleFace(Mod_SubdivMesh *mesh, int f);

/* Procedura che triangolarizza una face della mesh. */
int gen_triangulationOneFace(Mod_SubdivMesh *mesh, int f);

/* Funzione che effettua una triangolarizzazione virtuale di un poligono. */
int** gen_triangulationPoly(Mod_SubdivMesh *m, int f, int *nTri);

/* Funzione che suddivide un triangolo */
void gen_splitTriangle(Mod_SubdivMesh *mesh, int f, Vertex *m, int *index);

int gen_countIntersections(Mod_SubdivMesh *m_1, Mod_SubdivMesh *m_2, q_point *EM1_FM2, q_point *EM2_FM1, i_point* TmpD);

/* Procedura per la ricostruzione della poligonale definita
   dai punti di intersezione delle due mesh. */
i_point* gen_buildPolygonProfile(Mod_SubdivMesh *m_1, q_point **EM1_FM2, Mod_SubdivMesh *m_2, q_point **EM2_FM1, int *count, int *num, vector<int>* faceSplit, vector<int>* queueM, int *np, int nTan1, int nTan2);

/* Funzione che verifica se il punto di intersezione (i) appartiene alla
   face (f).
   Nel caso interFace sia NULL allora il punto deve appartenere esclusivamente
   a un edge (e) della face.
   Altrimenti può essere anche interno alla face. */
bool gen_checkIntersection(Mod_SubdivMesh *mesh, Vertex iv, int f, int *e, q_point *pEdge, q_point *pFace);

/* Procedura per l'inserimento di un profilo poligonale in una mesh,
   in modo da creare nuove face e nuovi edge. */
Mod_SubdivMesh* gen_insertProfile(Mod_SubdivMesh *mesh, int howMesh, i_point **profiles, int n, int current, int size, q_point *interEdge, q_point *interFace);

/* Funzione per verificare se i due punti sono stati generati da
   uno stesso edge. */
bool gen_onSameEdge(Mod_SubdivMesh *mesh, q_point *edgeMesh, Vertex p, Vertex q);

/* Funzione che verifica se esiste un edge della mesh su cui
   giacciono entrambi i punti */
bool gen_pointONsegment(Mod_SubdivMesh *mesh, Vertex p0, Vertex p1);

/* Procedura che separa una mesh in due mesh distinte seguendo
   un determinato profilo di taglio. */
Mod_SubdivMesh* gen_splitMesh(Mod_SubdivMesh *mesh, vector<Mod_SubdivMesh*> *nM, i_point **profiles, int k);

/* Funzione per la ricerca dei vertici vicini tra due mesh distinte:
   l'array delle coppie create viene dimensionato sul numero di vertici
   minore tra le due mesh e comunque sul numero di vertici della mesh 2. */
int* gen_searchVneighbour(Mod_SubdivMesh *mesh_1, Mod_SubdivMesh *mesh_2, float toll);

/* Funzione della coerenza delle normali tra due mesh unite
   TRUE = coerenza. */
bool gen_normalCheckDouble(Mod_SubdivMesh *mesh_1,Mod_SubdivMesh *mesh_2, int *vneigh);

/* Procedura per invertire l'orientamento delle normali relative alle facce. */
void gen_reverseNormal(Mod_SubdivMesh *mesh);

/* Procedura utilizzata per l'unione effettiva di due mesh. */
Mod_SubdivMesh* gen_glueCoupleMesh(Mod_SubdivMesh *mesh_1, Mod_SubdivMesh *mesh_2, int **vneigh);

/* Funzione per inglobare un insieme di mesh in un unica mesh */
Mod_SubdivMesh* gen_meshAdd(vector<Mod_SubdivMesh*> *nM, int m);

// Apply boolean operators on two meshes
vector<Mod_SubdivMesh*> gen_applyBooleanOps(Mod_SubdivMesh *mesh_1, Mod_SubdivMesh *mesh_2);

/* Procedura per la ricerca dei vertici incidenti su edge tra due mesh. */
void gen_searchEneighbour(Mod_SubdivMesh **mesh_1, Mod_SubdivMesh **mesh_2, float toll);

/* Funzione per la ricerca degli edge di bordo. */
e_bound* gen_findEbound(Mod_SubdivMesh *mesh);

/* Funzione per il riordino degli edge di bordo,
   in modo da creare il profilo reale. */
e_bound* gen_boundProfile(Mod_SubdivMesh *mesh, e_bound **QEB);

// Compute the axis aligned bounding box (AABB)
Vertex* gen_computeBoundingBox(Mod_SubdivMesh *mesh);

// Check if two AABB are colliding
bool gen_checkBoxCollision(Vertex *bb1, Vertex *bb2);

/* Funzione che determina se una faccia si trova in un bounding box */
bool gen_faceInBox(Mod_SubdivMesh *mesh, int f, Vertex *bb);

/* Funzione che,date due facce complanari,ne determina la regione di sovrapposizione e ne esegue lo split.
 * param[4] individua il piano in cui si trovano le due facce */
vector<Vertex> gen_faceOverlap(Mod_SubdivMesh **mesh1, Mod_SubdivMesh **mesh2, int f1, int f2, double *param, vector< vector<Vertex> > *seqA, vector< vector<Vertex> > *seqB);

// Individua le aree in tangenza tra due mesh
void gen_fTg(Mod_SubdivMesh **mesh1, Mod_SubdivMesh **mesh2);

// Inserisce un profilo di tangenza in una faccia
void gen_insertTanProfile(Mod_SubdivMesh *mesh, int f, vector<Vertex> profile, vector< vector< vector<Vertex> > > faceChains, int index, int flag);

/* Inserisce un punto in un edge,spezzandolo in due */
void gen_insPointOnEdge(Mod_SubdivMesh **mesh1, Vertex v, int edge);

/* Dati due insiemi di edge complanari, ne calcola le intersezioni */
vector<Vertex> gen_setIntersect(vector< vector<Vertex> > edge1, vector< vector<Vertex> > edge2);

/* Baricentro di una faccia */
Vertex gen_faceCenter(Mod_SubdivMesh *mesh,int f);

/* Funzione che, date due facce, determina quale delle due facce è interna all'altra */
int gen_faceInFace(Mod_SubdivMesh* mesh1,int f1,Mod_SubdivMesh* mesh2,int f2);

// Procedura che rimuove un punto da una lista di punti, non libera la memoria
bool removePoint(q_point *point,q_point **list, int *flag);

// swap two int
void swap_int(int *a, int *b);

// Print an error message
void ErrorMessage();


#endif // __Craftmesh_gengine_h__
